package com.itlong.cloud.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.itlong.cloud.service.RedisService;
import com.itlong.cloud.utils.node.JsonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;

import java.io.IOException;
import java.util.*;

/**
 * <desc>
 *      Redis工具实现类。
 * </desc>
 *
 * @createDate 2017/08/29
 */
@Service
public class RedisServiceImpl<T> implements RedisService<T> {

    protected static final Logger LOG = LoggerFactory.getLogger(RedisServiceImpl.class);

    @Autowired
    private RedisTemplate<String, ?> redisTemplate;


    /**
     * <desc>
     *      保存字符串类型对象.
     * <desc/>
     *
     * @param k 键
     * @param v 值
     * @return boolean
     * @author Wenchao.L
     * @createDate
     */
    @Override
    public boolean set(final String k, final String v) {
        boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
                connection.set(serializer.serialize(k), serializer.serialize(v));
                return true;
            }
        });
        return result;
    }


    /**
     * <desc>
     *      保存字符串类型对象.
     * <desc/>
     *
     * @param k 键
     * @param time 时间
     * @param v 值
     * @return boolean
     * @author Wenchao.L
     * @createDate
     */
    @Override
    public boolean set(final String k, long time, final String v) {
        boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
                connection.setEx(serializer.serialize(k), time, serializer.serialize(v));
                return true;
            }
        });
        return result;
    }


    /**
     * <desc>
     *      保存泛型对象.
     * <desc/>
     *
     * @param k 键
     * @param time 时间
     * @param t 值
     * @return boolean
     * @author Wenchao.L
     * @createDate
     */
    @Override
    public boolean set(String k, long time, T t) {
        boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
                try {
                    connection.setEx(serializer.serialize(k), time, serializer.serialize(JsonUtil.toJSON(t)));
                } catch (IOException ex) {
                    LOG.error("【Redis_保存异常】",ex);
                }
                return true;
            }
        });
        return result;
    }


    /**
     * <desc>
     *      获取泛型对象.
     * <desc/>
     *
     * @param k 键
     * @param t 对象
     * @return 泛型对象数据
     * @author Wenchao.L
     * @createDate
     */
    @Override
    public T getObject(String k, Class<T> t) {
        String result = redisTemplate.execute(new RedisCallback<String>() {
            @Override
            public String doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
                byte[] value = connection.get(serializer.serialize(k));
                return serializer.deserialize(value);
            }
        });
        try {
            if (result != null) {
                return JsonUtil.toObject(result, t);
            }
        } catch (IOException ex) {
            LOG.error("【Redis_获取对象缓存异常】",ex);
        }
        return null;
    }

    /**
     * <desc>
     *      获取泛型List对象.
     * <desc/>
     *
     * @param k 键
     * @param jsonTypeReference 对象
     * @return 泛型对象数据
     * @author Jiaqi.X
     * @createDate 2017/12/10
     */
    @Override
    public T decodeObject(String k, TypeReference<T> jsonTypeReference) {
        String result = redisTemplate.execute(new RedisCallback<String>() {
            @Override
            public String doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
                byte[] value = connection.get(serializer.serialize(k));
                return serializer.deserialize(value);
            }
        });
        if (result != null) {
            try {
                return JsonUtil.decode(result, jsonTypeReference);
            } catch (IOException ex) {
                LOG.error("【Redis_获取对象缓存异常】",ex);
            }
        }
        return null;
    }

    /**
     * <desc>
     *      获取字符串对象数据.
     * <desc/>
     *
     * @param k 键
     * @return 字符串对象数据
     * @author Wenchao.L
     * @createDate
     */
    public String get(final String k) {
        String result = redisTemplate.execute(new RedisCallback<String>() {
            @Override
            public String doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
                byte[] value = connection.get(serializer.serialize(k));
                return serializer.deserialize(value);
            }
        });
        return result;
    }

    /**
     * <desc>
     *     删除指定key的缓存对象数据
     * </desc>
     * @param k 键
     * @return
     * * @author cuiduanwu
     * @createDate 2017/10/10
     */
    @Override
    public boolean del(String k){
        boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
                connection.del(serializer.serialize(k));
                return true;
            }
        });
        return result;
    }
    /**
     * <desc>
     *     删除指定key的缓存对象数据
     * </desc>
     * @param k 键
     * @return
     * * @author cuiduanwu
     * @createDate 2017/10/10
     */
    @Override
    public boolean delForDevice(String k){
        Set<String> resultString = this.getKeys(k);
        for (String key : resultString) {
            this.del(key);
        }
        return true;
    }

    /**
     * <desc>
     *     批量获取相关前缀的key
     * </desc>
     * @param k 键
     * @return
     * * @author Qiang.S
     * @createDate 2019/01/18
     */
    @Override
    public Set<String> getKeys(String k){
        Set<byte[]> result = redisTemplate.execute(new RedisCallback<Set<byte[]>>() {
            @Override
            public Set<byte[]> doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
                Set<byte[]> value = connection.keys(serializer.serialize("*"+k+"*"));
                return value;
            }
        });
        Set<String> resultString = new HashSet<>();
        for (byte[] b:result) {
            resultString.add(new String(b));
        }
        return resultString;
    }

    /**
     * <desc>
     *     批量获取相关前缀的key
     * </desc>
     * @param k 键
     * @return
     * * @author Qiang.S
     * @createDate 2019/11/29
     */
    @Override
    public String getKeysForDevice(String k){
        Set<String> resultString = this.getKeys(k);
        if (resultString.size() > 0) {
            List<String> list = new ArrayList<>(resultString);
            Collections.sort(list);//排序
            String resultStr = this.get(list.get(list.size()-1));
            list.remove(list.size()-1);//删除最后一条key
            if (resultString.size() > 0) {
                for (String key : list) {//清除沉余数据
                    this.del(key);
                }
            }
            return resultStr;
        }
        return null;
    }


    /*
     * <desc>
     *       获取分布式锁，保存字符串类型对象（带时间）.
     * <desc/>
     *
     * @param k     键
     * @param time  过期时间
     * @param v     泛型对象
     * @return boolean
     * @author pengwei
     * @createDate 2020/11/03
     */
    @Override
    public boolean setNx(final String k, long time, final String v) {
        boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                try {
                    Jedis jedis = (Jedis) connection.getNativeConnection();
                    String  setResult = jedis.set(k,v,"NX","PX",time);
                    if(!"OK".equals(setResult)){
                            return  false;
                    }
                } catch (Exception ex) {
                    LOG.error("【Redis_保存异常】",ex);
                }
                return true;
            }
        });

        return result;
    }

    @Override
    public HashOperations<String, String, Object> getHash() {
        return redisTemplate.opsForHash();
    }

}
